feat: deeplinks support + Raycast extension#1734
feat: deeplinks support + Raycast extension#1734zhuanxqq wants to merge 1 commit intoCapSoftware:mainfrom
Conversation
| import { open } from "@raycast/api"; | ||
|
|
||
| export default async function Command() { | ||
| const value = JSON.stringify({ StopRecording: {} }); |
There was a problem hiding this comment.
Wrong serde variant names across all Raycast commands
DeepLinkAction is declared with #[serde(rename_all = "snake_case")], so every variant name is serialised as snake_case in JSON. All six Raycast commands use the original PascalCase names, which serde will never match, so every deeplink invocation silently fails with a parse error.
Same problem in pause-recording.tsx (PauseRecording), resume-recording.tsx (ResumeRecording), switch-microphone.tsx (SwitchMicrophone), switch-camera.tsx (SwitchCamera), and start-recording.tsx (StartRecording).
| const value = JSON.stringify({ StopRecording: {} }); | |
| const value = JSON.stringify({ stop_recording: {} }); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/raycast/src/stop-recording.tsx
Line: 4
Comment:
**Wrong serde variant names across all Raycast commands**
`DeepLinkAction` is declared with `#[serde(rename_all = "snake_case")]`, so every variant name is serialised as `snake_case` in JSON. All six Raycast commands use the original `PascalCase` names, which serde will never match, so every deeplink invocation silently fails with a parse error.
Same problem in `pause-recording.tsx` (`PauseRecording`), `resume-recording.tsx` (`ResumeRecording`), `switch-microphone.tsx` (`SwitchMicrophone`), `switch-camera.tsx` (`SwitchCamera`), and `start-recording.tsx` (`StartRecording`).
```suggestion
const value = JSON.stringify({ stop_recording: {} });
```
How can I resolve this? If you propose a fix, please make it concise.| const value = JSON.stringify({ | ||
| SwitchCamera: { | ||
| camera: props.arguments.camera, | ||
| }, | ||
| }); | ||
| await open(`cap://action?value=${encodeURIComponent(value)}`); |
There was a problem hiding this comment.
camera must be a DeviceOrModelID tagged object, not a bare string
The Rust field type is DeviceOrModelID, an enum serialised with serde's default external-tag format. Passing a plain string like "My Camera" will fail to deserialise. The correct payload for a device ID is { DeviceID: "..." } (or { ModelID: "..." } for model IDs).
| const value = JSON.stringify({ | |
| SwitchCamera: { | |
| camera: props.arguments.camera, | |
| }, | |
| }); | |
| await open(`cap://action?value=${encodeURIComponent(value)}`); | |
| const value = JSON.stringify({ | |
| switch_camera: { | |
| camera: { DeviceID: props.arguments.camera }, | |
| }, | |
| }); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/raycast/src/switch-camera.tsx
Line: 10-15
Comment:
**`camera` must be a `DeviceOrModelID` tagged object, not a bare string**
The Rust field type is `DeviceOrModelID`, an enum serialised with serde's default external-tag format. Passing a plain string like `"My Camera"` will fail to deserialise. The correct payload for a device ID is `{ DeviceID: "..." }` (or `{ ModelID: "..." }` for model IDs).
```suggestion
const value = JSON.stringify({
switch_camera: {
camera: { DeviceID: props.arguments.camera },
},
});
```
How can I resolve this? If you propose a fix, please make it concise.| export default async function Command() { | ||
| const value = JSON.stringify({ | ||
| StartRecording: { | ||
| capture_mode: { Screen: "" }, | ||
| camera: null, | ||
| mic_label: null, | ||
| capture_system_audio: false, | ||
| mode: "Studio", | ||
| }, | ||
| }); | ||
| await open(`cap://action?value=${encodeURIComponent(value)}`); |
There was a problem hiding this comment.
Nested serde renames also use the wrong case
Beyond the outer variant name (StartRecording → start_recording), two inner types have their own rename_all rules:
CaptureModehasrename_all = "snake_case"→Screenmust bescreenRecordingModehasrename_all = "camelCase"→Studiomust bestudio
All three mismatches together mean this command will always fail to parse.
| export default async function Command() { | |
| const value = JSON.stringify({ | |
| StartRecording: { | |
| capture_mode: { Screen: "" }, | |
| camera: null, | |
| mic_label: null, | |
| capture_system_audio: false, | |
| mode: "Studio", | |
| }, | |
| }); | |
| await open(`cap://action?value=${encodeURIComponent(value)}`); | |
| export default async function Command() { | |
| const value = JSON.stringify({ | |
| start_recording: { | |
| capture_mode: { screen: "" }, | |
| camera: null, | |
| mic_label: null, | |
| capture_system_audio: false, | |
| mode: "studio", | |
| }, | |
| }); | |
| await open(`cap://action?value=${encodeURIComponent(value)}`); | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/raycast/src/start-recording.tsx
Line: 3-13
Comment:
**Nested serde renames also use the wrong case**
Beyond the outer variant name (`StartRecording` → `start_recording`), two inner types have their own `rename_all` rules:
- `CaptureMode` has `rename_all = "snake_case"` → `Screen` must be `screen`
- `RecordingMode` has `rename_all = "camelCase"` → `Studio` must be `studio`
All three mismatches together mean this command will always fail to parse.
```suggestion
export default async function Command() {
const value = JSON.stringify({
start_recording: {
capture_mode: { screen: "" },
camera: null,
mic_label: null,
capture_system_audio: false,
mode: "studio",
},
});
await open(`cap://action?value=${encodeURIComponent(value)}`);
}
```
How can I resolve this? If you propose a fix, please make it concise.| StartRecording: { | ||
| capture_mode: { Screen: "" }, | ||
| camera: null, | ||
| mic_label: null, |
There was a problem hiding this comment.
Hardcoded screen/mode with no user control
capture_mode is hard-coded to Screen with an empty display name (""), and mode is hard-coded to "Studio". An empty display name causes the Rust handler to search list_displays() for a display whose name == "", which will not match any real display and returns an error. The start-recording command should either accept arguments for these fields or use a sensible default like the primary display name.
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/raycast/src/start-recording.tsx
Line: 5-8
Comment:
**Hardcoded screen/mode with no user control**
`capture_mode` is hard-coded to `Screen` with an empty display name (`""`), and `mode` is hard-coded to `"Studio"`. An empty display name causes the Rust handler to search `list_displays()` for a display whose `name == ""`, which will not match any real display and returns an error. The `start-recording` command should either accept arguments for these fields or use a sensible default like the primary display name.
How can I resolve this? If you propose a fix, please make it concise.
Closes #1540
This PR adds new deeplink actions for recording controls and includes a Raycast extension.
Changes:
/claim #1540
Greptile Summary
This PR extends the Tauri deeplink system with pause/resume recording and microphone/camera switching actions, and ships a Raycast extension that triggers them. The Rust side is correct, but all six Raycast commands will silently fail at runtime due to serde serialisation mismatches.
StopRecording) butDeepLinkActionhas#[serde(rename_all = "snake_case")], so JSON keys must be snake_case (stop_recording,pause_recording, etc.).start-recording.tsxadditionally mismatchesCaptureMode(Screen→screen) andRecordingMode(Studio→studio), and passes an empty display name that will never match a real screen.switch-camera.tsxpasses the camera as a plain string, but the Rust field type isDeviceOrModelID, an externally-tagged enum that expects{ "DeviceID": "..." }or{ "ModelID": ... }.Confidence Score: 3/5
Important Files Changed
Sequence Diagram
sequenceDiagram participant User participant Raycast participant macOS participant TauriApp as Cap (Tauri) participant Rust as deeplink_actions.rs participant Recording as recording.rs User->>Raycast: Trigger command (e.g. pause-recording) Raycast->>Raycast: "JSON.stringify({ pause_recording: {} })" Raycast->>macOS: "open("cap://action?value=...")" macOS->>TauriApp: Deep link URL TauriApp->>Rust: handle(urls) Rust->>Rust: DeepLinkAction::try_from(url) Rust->>Rust: serde_json::from_str(json_value) Rust->>Recording: pause_recording / resume_recording / set_mic_input / set_camera_input Recording-->>TauriApp: Ok(()) or Err(String)Prompt To Fix All With AI
Reviews (1): Last reviewed commit: "feat: add pause/resume/switch deeplinks ..." | Re-trigger Greptile
(2/5) Greptile learns from your feedback when you react with thumbs up/down!